#include "camera_device.h"
#include "axil_cmd.h"
#include "sensor_config.h"
#include "sensor_spi.h"
#include "sensor_user.h"
#include "libaxidma.h"
#include "device_constants.h"
#include <QThread>
#include <QTimer>
//test
#include <QDebug>


namespace  {

//gpio
axil_cmd_handle axil_cmd_user;
axil_cmd_handle axil_cmd_spi;
axil_cmd_handle axil_cmd_gpga;

bool dma_success_flag = false;

//回调函数，当捕获到图像数据时被调用
void callbackafterrecieve(int channel_id, void *data){
    (void)channel_id;
    (void)data;
    dma_success_flag = true;
}

}

CameraDevice::CameraDevice()
{    
    image_ptr_ = (unsigned char *)malloc(IMAGE_SIZE);
    //检测标志位是否变化
    image_capture_time_ = new QTimer;
    connect(image_capture_time_, &QTimer::timeout, this, &CameraDevice::onSendCaptureImageSignal);
    image_capture_time_->start(100);
}

CameraDevice::~CameraDevice()
{

}

int CameraDevice::cameraInit()
{
    qDebug() << "cameraInit";
    //地址映射
    axil_cmd_setup(&axil_cmd_user, USR_CMD_ADDR);
    axil_cmd_setup(&axil_cmd_spi, SPI_CMD_ADDR);
    axil_cmd_setup(&axil_cmd_gpga, FPGA_MONITOR);

    //初始化
    int result;
    result = initializeSensor();
    if(result) return result;
    result = initializeFPGA();
    if(result) return result;
    result = initializeCameraParameter();
    if(result) return result;
    result = initializeDMA();
    if(result) return result;

    return DEVICE_OK;
}

int CameraDevice::cameraReset()
{
    return DEVICE_OK;
}

int CameraDevice::trigger()
{
    unsigned int trigger_number = 0;
    trigger_number = axil_cmd_get(&axil_cmd_user, USER_TRIGGER_SUCCESS_CNT);
    axil_cmd_set(&axil_cmd_user,USER_TRIGGER,0x1);
    //读取成功触发sensor的的数量是否增长
    if(axil_cmd_get(&axil_cmd_user, USER_TRIGGER_SUCCESS_CNT) > trigger_number){
        return DEVICE_OK;
    }else{
        return TRIGGER_ERR;
    }

}

int CameraDevice::setTriggerSource(unsigned char source)
{
    axil_cmd_set(&axil_cmd_user,USER_CAMERA_MODE,0x0);
    axil_cmd_set(&axil_cmd_user,USER_STOP_EXP,0x1);

    if(source == InternalTrigger){
        axil_cmd_set(&axil_cmd_user,USER_PHOTO_MODE,0x0);
    }else if(source == ExternalTrigger){
        axil_cmd_set(&axil_cmd_user,USER_PHOTO_MODE,0x2);
    }

    axil_cmd_set(&axil_cmd_user,USER_CAMERA_MODE,0x1);

    return DEVICE_OK;
}

unsigned char CameraDevice::getTriggerSource() const
{
    unsigned int ret = axil_cmd_get(&axil_cmd_user,USER_PHOTO_MODE);
    if(ret == 0x0){
        return InternalTrigger;
    }else if(ret == 0x2){
        return ExternalTrigger;
    }
    return DEVICE_ERR;
}

int CameraDevice::setExposureTime(unsigned short time)
{
    if(time < exposure_low_limit_ || time > exposure_high_limit_){
        qInfo() << "setExposureTime parameter out of range";
        return DEVICE_ERR;
    }
    axil_cmd_set(&axil_cmd_user,USER_CAMERA_MODE,0x0);

    // 曝光时间（单位ns） = (1140 + 设置数字) * 50，其中设置数字最小为5，其中50为调节精度50ns
//    unsigned int input_time = time*1000/50-1140;
    unsigned int input_time = time * 20-1140;
    axil_cmd_set(&axil_cmd_user,USER_TEXP0_EXP_LOW,input_time);
    axil_cmd_set(&axil_cmd_user,USER_TEXP0_EXP_HIGH,0x00);

    //gap时间比曝光时间多300ms
//    unsigned int gap_time = (time + 300) * 1000 / 50 - 1140;
    unsigned int gap_time = time * 20 + 4860;
    axil_cmd_set(&axil_cmd_user,USER_TEXP0_GAP_LOW,gap_time);
    axil_cmd_set(&axil_cmd_user,USER_TEXP0_GAP_HIGH,0x0);

    axil_cmd_set(&axil_cmd_user,USER_CAMERA_MODE,0x1);

    return DEVICE_OK;
}

unsigned short CameraDevice::getExposureTime() const
{
    unsigned int output_time = axil_cmd_get(&axil_cmd_user,USER_TEXP0_EXP_LOW);
//    return static_cast<unsigned short>((output_time+1140) * 50 / 1000);
    return static_cast<unsigned short>(0.05 * output_time + 57);
}

int CameraDevice::setAnologGain(unsigned char gain)
{
    if(gain < anolog_gain_low_limit_ || gain > 50){
        qInfo() << "setAnologGain parameter out of range";
        return DEVICE_ERR;
    }

    unsigned int register_address = 103;//103为sensor芯片手册中的103号寄存器
    sensor_spi_set(&axil_cmd_spi, SPI_CMD_OFFSET, SPI_STATUS_OFFSET, register_address, gain);
    return DEVICE_OK;
}

unsigned char CameraDevice::getAnologGain() const
{
    unsigned int register_address = 103;//103为sensor芯片手册中的103号寄存器
    unsigned int c = sensor_spi_get(&axil_cmd_spi, SPI_CMD_OFFSET, SPI_STATUS_OFFSET, SPI_RX_OFFSET, register_address);
    unsigned char ret = c & 0x3F;
    return ret;
}

 short CameraDevice::getSensorTemperature() const
{
    unsigned int temperature = axil_cmd_get(&axil_cmd_user,USER_SENSOR_TEMP);
    if((temperature >> 15) & 0x1){
//        qDebug() << "getSensorTemperature" << -(((~static_cast<short>(temperature >> 3) & 0xfff)+ 1) >> 4);
        return -(((~static_cast<short>(temperature >> 3) & 0xfff)+ 1) >> 4);
    }else{
        return static_cast<short>(temperature >> 7);
    }
 }

 short CameraDevice::getFpgaBoardTemperature() const
 {
     unsigned int temperature = axil_cmd_get(&axil_cmd_gpga,0x400);
//     qDebug() << "getFpgaBoardTemperature" << static_cast<short>(((temperature * 509) >> 16) - 280);
     return static_cast<short>(((temperature * 509) >> 16) - 280);
 }

int CameraDevice::setpictureMode(unsigned char mode)
{
    unsigned int register_address = 1;//1为sensor芯片手册中的1号寄存器
    if(mode == Test){
        sensor_spi_set(&axil_cmd_spi, SPI_CMD_OFFSET, SPI_STATUS_OFFSET, register_address, mode);
    }else if(mode == Actual){
        sensor_spi_set(&axil_cmd_spi, SPI_CMD_OFFSET, SPI_STATUS_OFFSET, register_address, 0x0);
    }
    return DEVICE_OK;
}

unsigned char CameraDevice::getPictureMode() const
{
    unsigned int register_address = 1;//1为sensor芯片手册中的1号寄存器
    unsigned int status = sensor_spi_get(&axil_cmd_spi, SPI_CMD_OFFSET, SPI_STATUS_OFFSET, SPI_RX_OFFSET, register_address);
    if((status&0x1) == 1){
        return Test;
    }else if((status&0x1) == 0){
        return Actual;
    }
    return DEVICE_ERR;
}

int CameraDevice::setFrameRate(unsigned short rate)
{
    (void)rate;
    return DEVICE_OK;
}

unsigned short CameraDevice::getFrameRate() const
{
    unsigned short ret = 1000;
    return ret;
}

int CameraDevice::initializeSensor()
{
    //复位，首先对USER_RESET_SENSOR位写入0x1，然后等待最少10ms，才能配置sensor
    axil_cmd_set(&axil_cmd_user,USER_RESET_SENSOR,0x1);
    QThread::usleep(10000);

    //初始化传感器寄存器
    axil_cmd_set(&axil_cmd_spi,SPI_RESET_OFFSET,0x1);
    for(uint i=0;i<370;i++){
        sensor_spi_set(&axil_cmd_spi, SPI_CMD_OFFSET, SPI_STATUS_OFFSET, i, sensor_config[i]);
    }

    //设置传感器输出训练字
    axil_cmd_set(&axil_cmd_spi,SPI_CMD_OFFSET,((0x3<<17) | 0x57));
    while(((axil_cmd_get(&axil_cmd_spi,SPI_STATUS_OFFSET) >> 26) & 0x1) != 0);

    return DEVICE_OK;
}

int CameraDevice::initializeFPGA()
{
    //最开始等至少500ms，然后对FPGA内部逻辑进行复位，然后开始训练，直到训练成功
    //然后判断是否对齐，如果未对齐，则重新复位，重新进行接口训练，最多十次。如果十次都失败，则报告接口训练错误
    QThread::usleep(500000);
    for(int i = 0; i <= 10; i++){
        //复位fpga的 RTL 电路
        axil_cmd_set(&axil_cmd_user,USER_RESET_RTL,0x0);
        QThread::usleep(10);
        axil_cmd_set(&axil_cmd_user,USER_RESET_RTL,0x1);
        QThread::usleep(1000);

        //fpga接口训练
        axil_cmd_set(&axil_cmd_user,USER_START_TRAINING,0x1);
        QThread::usleep(10000);
        qInfo() << "USER_TRAINING_STATUS";
        if(axil_cmd_get(&axil_cmd_user,USER_TRAINING_STATUS) == 0x3fff0003){
            qInfo() << "USER_TRAINING_STATUS SUCCESS";
            break;
        }
        if(i == 10)
        {
            qCritical() << "interface word align error";
            return FPGA_INITIALIZE_ERR;
        }
    }

    //清空计数器
    axil_cmd_set(&axil_cmd_user,USER_CLR_TRIGGER,0x1);

    //指示上位机配置完毕,fpga可以触发控制
    axil_cmd_set(&axil_cmd_user,USER_INIT_OK,0x1);

    return DEVICE_OK;
}

int CameraDevice::initializeCameraParameter()
{
    //设置曝光时间
    axil_cmd_set(&axil_cmd_user,USER_TEXP0_EXP_LOW,0x1822C);// 曝光时间（单位ns） = (1140 + 设置数字) * 50，其中设置数字最小为5，其中50为调节精度50ns
    axil_cmd_set(&axil_cmd_user,USER_TEXP0_EXP_HIGH,0x00);

    //设置帧时间控制,理论上设置曝光时间+300ms以后才能触发下一次曝光，才会让sensor输出正确的数据
    axil_cmd_set(&axil_cmd_user,USER_TEXP0_GAP_LOW,0x1822C+0x5B8D80);//+300ms  unit 50ns
    axil_cmd_set(&axil_cmd_user,USER_TEXP0_GAP_HIGH,0x0);

    //设置一次dma所需要接收的字节数
    axil_cmd_set(&axil_cmd_user,USER_DMA_CNT,IMAGE_SIZE); //set once dma bytes 8424*6032*2 Bytes

    //设置传感器工作模式：触发模式、拍照模式
    axil_cmd_set(&axil_cmd_user,USER_CAMERA_MODE,0x1);
    axil_cmd_set(&axil_cmd_user,USER_PHOTO_MODE,0x0);

    return DEVICE_OK;
}

int CameraDevice::initializeDMA()
{
    //初始化DMA设备
    axidma_dev_ = axidma_init();
    if (axidma_dev_ == nullptr) {
        qCritical() << "Failed to initialize the AXI DMA device.";
        return DMA_INITIALIZE_ERR;
    }
    rx_buf_ = static_cast<unsigned char*>(axidma_malloc(axidma_dev_, DMA_BLOCK_SIZE));
    if (rx_buf_ == nullptr) {
        qCritical() << "Unable to allocate receive buffer from the AXI DMA device";
        axidma_free(axidma_dev_, rx_buf_, DMA_BLOCK_SIZE);
        axidma_destroy(axidma_dev_);
        return DMA_INITIALIZE_ERR;
    }
    QThread::usleep(10000);
    rx_chans_ = axidma_get_dma_rx(axidma_dev_);
    if (rx_chans_->len < 1) {
        qCritical() << "Error: No receive channels were found.";
        axidma_free(axidma_dev_, rx_buf_, DMA_BLOCK_SIZE);
        axidma_destroy(axidma_dev_);
        return DMA_INITIALIZE_ERR;
    }
    qInfo() << "Using receive channel." << rx_chans_->data[0];
    axidma_set_callback(axidma_dev_,rx_chans_->data[0],callbackafterrecieve,nullptr);

    int ret = 0;
    //触发 AXI DMA 控制器执行一次单向数据传输，将指定内存缓冲区的数据搬运到 FPGA（发送方向），或从 FPGA 接收数据到内存（接收方向）
    ret = axidma_oneway_transfer(axidma_dev_,rx_chans_->data[0],rx_buf_,DMA_BLOCK_SIZE,false);
    if(ret != 0){
        return DMA_ERR;
    }
    axil_cmd_set(&axil_cmd_user,USER_DMA_RDY,0x1);

    return DEVICE_OK;
}

void CameraDevice::onSendCaptureImageSignal()
{
    if(dma_success_flag){
        dma_success_flag = false;
        axidma_oneway_transfer(axidma_dev_,rx_chans_->data[0],rx_buf_,DMA_BLOCK_SIZE,false);
        axil_cmd_set(&axil_cmd_user,USER_DMA_RDY,0x1);
        memcpy(image_ptr_,rx_buf_,DMA_BLOCK_SIZE);
        emit sigImageCaptured(image_ptr_);
    }
}


